package com.cmc.cloud.cmclink.doc.api.document;

import cn.hutool.core.collection.CollectionUtil;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.api.document.req.so.SoCancelReqDto;
import com.cmc.cloud.cmclink.doc.api.document.req.so.sovo.sovo.SoCreateAndUpdateReqDTO;
import com.cmc.cloud.cmclink.doc.api.document.req.so.sovo.sovo.SoDetailCompareRespDTO;
import com.cmc.cloud.cmclink.doc.api.document.req.soamendapplyvo.SoAmendApplyRespVO;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.SoErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.SoAmendApplyConvert;
import com.cmc.cloud.cmclink.doc.convert.SpecialCargoVoyageConvert;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.BlnoMapper;
import com.cmc.cloud.cmclink.doc.mapper.SoAmendApplyMapper;
import com.cmc.cloud.cmclink.doc.mapper.SpecialCargoVoyageMapper;
import com.cmc.cloud.cmclink.doc.service.SpecialCargoApplyService;
import com.cmc.cloud.cmclink.doc.service.VoyageBookingTimeService;
import com.cmc.cloud.cmclink.doc.service.VoyageBsaService;
import com.cmc.cloud.cmclink.doc.service.impl.so.SoVslvoyServiceImpl;
import com.cmc.cloud.cmclink.doc.service.si.VoyageLockService;
import com.cmc.cloud.cmclink.doc.service.so.BizSoService;
import com.cmc.cloud.cmclink.doc.service.so.OperateLogService;
import com.cmc.cloud.cmclink.doc.service.so.SoService;
import com.cmc.cloud.cmclink.doc.util.RedisUtils;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovo.SoCancelReqVo;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovo.SoCreateAndUpdateReqVo;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovo.SoDetailCompareRespVo;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovslvoyvo.SoVslvoyUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.specialcargoapplyvo.SpecialCargoVoyageUpdateReqVO;
import com.cmes.framework.common.exception.ErrorCode;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.CommonResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cmes.framework.security.core.util.SecurityFrameworkUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author：Jie Ni
 * @Date: 2025/10/1 13:46
 * @Description:
 */
@RestController
@RequiredArgsConstructor
public class SoAmendApplyInfoApiImpl implements SoAmendApplyInfoApi {

    private final SoService soService;

    private final SoAmendApplyMapper soAmendApplyMapper;

    private final BizSoService bizSoService;

    private final VoyageLockService voyageLockService;

    private final SoVslvoyServiceImpl soVslvoyService;

    private final SpecialCargoApplyService specialCargoApplyService;

    private final VoyageBsaService voyageBsaService;

    private final VoyageBookingTimeService voyageBookingTimeService;

    private final RedisUtils redisUtils;

    private final SpecialCargoVoyageMapper specialCargoVoyageMapper;

    private final BlnoMapper blnoMapper;

    private final OperateLogService operateLogService;

    private static final String SI_CUT_OFF = "si_cut_off";


    @Override
    public CommonResult<SoAmendApplyRespVO> getSoAmendApply(Long soId) {
        SoDO oldSo = soService.getSo(soId);
        if (oldSo == null) {
            throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_NOT_EXISTS);
        }
        // 本人才可以操作
        checkUserLimit(oldSo.getCreator(), String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        if (!Objects.equals(SoStatusEnum.BookingConfirmed.getValue(), oldSo.getSoStatus())) {
            throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_UPDATE_CONFIRMED_ERROR);
        }
        LambdaQueryWrapperX<SoAmendApplyDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(SoAmendApplyDO::getSoId, soId);
        queryWrapper.inIfPresent(SoAmendApplyDO::getSoAmendStatus, Arrays.asList(ContentChangeStatusEnum.BOOKING_CHANGE_PROCESSING.getValue(), ContentChangeStatusEnum.BOOKING_CANCEL_PROCESSING.getValue()));
        queryWrapper.last("LIMIT 1");
        SoAmendApplyDO data = soAmendApplyMapper.selectOne(queryWrapper);
        return CommonResult.success(SoAmendApplyConvert.INSTANCE.convert(data));
    }

    @Override
    public CommonResult<Void> revertSoAmendApply(Long amendApplyId) {
        bizSoService.amendCancel(amendApplyId, String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        return CommonResult.success();
    }

    public void checkUserLimit(String oldUserId, String currentUserId) {
        if (!Objects.equals(currentUserId, oldUserId)) {
            throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_UPDATE_LIMIT);
        }
    }

    public CommonResult<Long> changeVesselInfoForWeb(SoCreateAndUpdateReqDTO soCreateAndUpdateReqDTO) {
        SoCreateAndUpdateReqVo reqVo = SoAmendApplyConvert.INSTANCE.convert(soCreateAndUpdateReqDTO);
        return CommonResult.success(bizSoService.updateConfirmedSoAmendVessel(reqVo, String.valueOf(SecurityFrameworkUtils.getLoginUserId())));
    }

    @Override
    public CommonResult<Long> changeSoInfoForWeb(SoCreateAndUpdateReqDTO soCreateAndUpdateReqDTO) {
        SoCreateAndUpdateReqVo reqVo = SoAmendApplyConvert.INSTANCE.convert(soCreateAndUpdateReqDTO);
        return CommonResult.success(bizSoService.updateConfirmedSoAmend(reqVo, String.valueOf(SecurityFrameworkUtils.getLoginUserId())));
    }

    @Override
    public CommonResult<SoDetailCompareRespDTO> compare(Long amendApplyId) {
        SoDetailCompareRespVo soDetailCompareRespVo = bizSoService.getAmendApplyDetail(amendApplyId);
        // 填充拒绝理由
        if(soDetailCompareRespVo.getAmendApply() != null && Objects.equals(soDetailCompareRespVo.getAmendApply().getSoAmendStatus(), ContentChangeStatusEnum.BOOKING_CHANGE_REJECTED.getValue())) {
            // 订舱修正拒绝的话需要返回理由
            SoDO so = soService.getSo(soDetailCompareRespVo.getAmendApply().getSoId());
            if(so != null && so.getOperateLogId() != null){
                OperateLogDO operateLog = operateLogService.getOperateLog(so.getOperateLogId());
                soDetailCompareRespVo.getAmendApply().setRejectedReason(operateLog != null ? operateLog.getOperateDesc() : null);
            }
        }
        SoDetailCompareRespDTO soDetailCompareRespDTO = SoAmendApplyConvert.INSTANCE.convert(soDetailCompareRespVo);
        return CommonResult.success(soDetailCompareRespDTO);
    }

    /**
     * 更新特种货航线信息
     * @param soDO so信息
     * @param newSpecialCargoVslvoyList 新的航线数据
     */
    public void updateSpecialCargoVslvoy(SoDO soDO, List<SpecialCargoVoyageUpdateReqVO> newSpecialCargoVslvoyList) {
        SpecialCargoApplyDO applyDO = specialCargoApplyService.getByBookingNo(soDO.getBookingNo());
        // 特种货存在且申请状态不为已审批和拒绝才能修改船期
        if (null != applyDO && CollectionUtil.isNotEmpty(newSpecialCargoVslvoyList) && !ApplyStatusEnum.APPROVED.getValue().equals(applyDO.getApplyStatus()) && !ApplyStatusEnum.REJECT.getValue().equals(applyDO.getApplyStatus())) {
            String routeApproveStatus = null;
            List<SpecialCargoVoyageDO> oldSpecialCargoVslvoyList = specialCargoVoyageMapper.selectList(SpecialCargoVoyageDO::getApplyId, applyDO.getId());
            if(CollectionUtil.isNotEmpty(oldSpecialCargoVslvoyList)){
                routeApproveStatus = oldSpecialCargoVslvoyList.get(0).getRouteApproveStatus();
                // 老数据删除
                specialCargoVoyageMapper.deleteBatchIds(oldSpecialCargoVslvoyList.stream().map(SpecialCargoVoyageDO::getId).collect(Collectors.toList()));
            }
            // 新数据批量插入
            List<SpecialCargoVoyageDO> voyageDoList = SpecialCargoVoyageConvert.INSTANCE.convertDOList(newSpecialCargoVslvoyList);
            for (SpecialCargoVoyageDO item : voyageDoList) {
                item.setApplyId(applyDO.getId());
                item.setCarrier(CntrOwnerEnum.SNL.getValue());
                item.setRouteApproveStatus(routeApproveStatus);
            }
            specialCargoVoyageMapper.insertBatch(voyageDoList);

        }
    }

    public void checkVslvoy(List<SoVslvoyUpdateReqVO> soVslvoyList) {
        if (CollectionUtil.isNotEmpty(soVslvoyList)) {
            for (SoVslvoyUpdateReqVO e : soVslvoyList) {
                // 网厅也只校验第一层liner
                if (TransmodeEnum.LINER.getValue().equals(e.getTransmode())) {
                    VoyageLockDO voyageLock = voyageLockService.findVoyageLock(e.getVesselCode(), e.getVoyageEx(), e.getPolCode(), ExImTypeEnum.EX.getValue());
                    if (voyageLock != null && voyageLock.getVoyageLockFlag()) {
                        throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_VOYAGE_LOCK_ERROR);
                    }
                    VoyageBsaDO voyageBsa = voyageBsaService.findVoyageBsa(e.getVesselCode(), e.getVoyageEx(), e.getPolCode(), ExImTypeEnum.EX.getValue());
                    if (voyageBsa != null && Arrays.asList(BookingOpenStatusEnum.WAITING.getValue(), BookingOpenStatusEnum.CLOSE.getValue()).contains(voyageBsa.getBookingOpenStatus())) {
                        throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_VOYAGE_BSA_ERROR);
                    }
                    checkVoyageBookingTime(e.getVesselCode(), e.getVoyageEx(), e.getPolCode(), SI_CUT_OFF);
                    // 走到这里就结束
                    return;
                }
            }
        }
    }

    public void checkVoyageBookingTime(String vesselCode, String voyage, String polCode, String timeType) {
        //在原航次截关之前（Customs Cut Off 之前
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        VoyageBookingTimeDO voyageBookingTime = voyageBookingTimeService.getVoyageBookingTimeByVesselCodeAndVoyage(vesselCode, voyage, polCode, "EX", timeType, LocalDateTime.now().format(formatter));
        if (voyageBookingTime != null) {
            throw ServiceExceptionUtil.exception(new ErrorCode(SoErrorCodeConstants.SO_CHANGE_BKG_VESSEL_SI_CUT_OFF_ERROR.getCode(), SoErrorCodeConstants.SO_CHANGE_BKG_VESSEL_SI_CUT_OFF_ERROR.getMsg() + voyageBookingTime.getTimeValue()));
        }
    }

}
